import { beforeEach, describe, expect, it } from "vitest";
import { CmsGroup } from "~/types";
import { useGraphQLHandler } from "../testHelpers/useGraphQLHandler";
import models from "./mocks/contentModels";
import { useCategoryManageHandler } from "../testHelpers/useCategoryManageHandler";
import { useCategoryReadHandler } from "../testHelpers/useCategoryReadHandler";
import type { IdentityData } from "@webiny/api-core/features/IdentityContext";

const createIdentity = (permissions: any[] = []): IdentityData => {
    return {
        id: "a1234567890",
        displayName: "API",
        type: "api-key",
        permissions: [
            {
                name: "content.i18n",
                locales: ["en-US"]
            },
            {
                name: "cms.endpoint.read"
            },
            {
                name: "cms.contentModelGroup",
                rwd: "r"
            },
            {
                name: "cms.contentModel",
                rwd: "r"
            }
        ].concat(permissions)
    };
};

describe("READ - resolvers - api key", () => {
    let contentModelGroup: CmsGroup;

    const API_TOKEN = "aToken";

    const manageOpts = {
        path: "manage/en-US"
    };
    const readOpts = { path: "read/en-US", permissions: [] };

    const {
        createContentModelMutation,
        updateContentModelMutation,
        createContentModelGroupMutation
    } = useGraphQLHandler(manageOpts);

    beforeEach(async () => {
        const [createCMG] = await createContentModelGroupMutation({
            data: {
                name: "Group",
                slug: "group",
                icon: "ico/ico",
                description: "description"
            }
        });
        contentModelGroup = createCMG.data.createContentModelGroup.data;

        const category = models.find(m => m.modelId === "category");
        if (!category) {
            throw new Error(`Could not find model "category".`);
        }

        // Create initial record
        const [create] = await createContentModelMutation({
            data: {
                name: category.name,
                modelId: category.modelId,
                singularApiName: category.singularApiName,
                pluralApiName: category.pluralApiName,
                group: contentModelGroup.id
            }
        });

        if (create.errors) {
            console.error(`[beforeEach] ${create.errors[0].message}`);
            process.exit(1);
        }

        const [update] = await updateContentModelMutation({
            modelId: create.data.createContentModel.data.modelId,
            data: {
                fields: category.fields,
                layout: category.layout
            }
        });

        if (update.errors) {
            console.error(`[beforeEach] ${update.errors[0].message}`);
            process.exit(1);
        }
    });

    it("get entry", async () => {
        // Use "manage" API to create and publish entries
        const { createCategory, publishCategory } = useCategoryManageHandler(manageOpts);

        // Create an entry
        const [create] = await createCategory({ data: { title: "Title 1", slug: "slug-1" } });
        const category = create.data.createCategory.data;
        const { id: categoryId } = category;

        // Publish it so it becomes available in the "read" API
        const [publishedCategoryResponse] = await publishCategory({ revision: categoryId });

        const publishedCategory = publishedCategoryResponse.data.publishCategory.data;

        // See if entries are available via "read" API
        const { getCategory } = useCategoryReadHandler({
            ...readOpts,
            identity: createIdentity([
                {
                    name: "cms.contentEntry",
                    rwd: "r"
                }
            ])
        });

        const queryArgs = {
            where: {
                id: categoryId
            }
        };
        const headers = {
            authorization: API_TOKEN
        };

        const [result] = await getCategory(queryArgs, headers);

        expect(result).toEqual({
            data: {
                getCategory: {
                    data: {
                        id: category.id,
                        entryId: category.entryId,
                        createdOn: category.createdOn,
                        savedOn: publishedCategory.savedOn,
                        title: category.title,
                        slug: category.slug
                    },
                    error: null
                }
            }
        });
    });

    it("get entries", async () => {
        // Use "manage" API to create and publish entries
        const { createCategory, publishCategory } = useCategoryManageHandler(manageOpts);

        // Create an entry
        const [create] = await createCategory({ data: { title: "Title 1", slug: "slug-1" } });
        const category = create.data.createCategory.data;
        const { id: categoryId } = category;

        // Publish it so it becomes available in the "read" API
        const [publishCategoryResponse] = await publishCategory({ revision: categoryId });

        const publishedCatgory = publishCategoryResponse.data.publishCategory.data;

        // See if entries are available via "read" API
        const { listCategories } = useCategoryReadHandler({
            ...readOpts,
            identity: createIdentity([
                {
                    name: "cms.contentEntry",
                    rwd: "r"
                }
            ])
        });

        const queryArgs = {
            where: {
                id: categoryId
            }
        };
        const headers = {
            authorization: API_TOKEN
        };

        const [result] = await listCategories(queryArgs, headers);

        expect(result).toEqual({
            data: {
                listCategories: {
                    data: [
                        {
                            id: category.id,
                            createdOn: category.createdOn,
                            savedOn: publishedCatgory.savedOn,
                            title: category.title,
                            slug: category.slug
                        }
                    ],
                    meta: {
                        cursor: null,
                        hasMoreItems: false,
                        totalCount: 1
                    },
                    error: null
                }
            }
        });
    });

    it("cant get entry - missing whole permission", async () => {
        // Use "manage" API to create and publish entries
        const { createCategory, publishCategory } = useCategoryManageHandler(manageOpts);

        // Create an entry
        const [create] = await createCategory({ data: { title: "Title 1", slug: "slug-1" } });
        const category = create.data.createCategory.data;
        const { id: categoryId } = category;

        // Publish it so it becomes available in the "read" API
        await publishCategory({ revision: categoryId });

        // See if entries are available via "read" API
        const { getCategory } = useCategoryReadHandler({
            ...readOpts,
            identity: createIdentity()
        });

        const queryArgs = {
            where: {
                id: categoryId
            }
        };
        const headers = {
            authorization: API_TOKEN
        };

        const [result] = await getCategory(queryArgs, headers);

        expect(result).toMatchObject({
            data: {
                getCategory: {
                    data: null,
                    error: {
                        code: "NOT_AUTHORIZED",
                        message: 'Not allowed to access "category" entries.'
                    }
                }
            }
        });
    });

    it("cant list entries - missing whole permission", async () => {
        // Use "manage" API to create and publish entries
        const { createCategory, publishCategory } = useCategoryManageHandler(manageOpts);

        // Create an entry
        const [create] = await createCategory({ data: { title: "Title 1", slug: "slug-1" } });
        const category = create.data.createCategory.data;
        const { id: categoryId } = category;

        // Publish it so it becomes available in the "read" API
        await publishCategory({ revision: categoryId });

        // See if entries are available via "read" API
        const { listCategories } = useCategoryReadHandler({
            ...readOpts,
            identity: createIdentity()
        });

        const queryArgs = {
            where: {
                id: categoryId
            }
        };
        const headers = {
            authorization: API_TOKEN
        };

        const [result] = await listCategories(queryArgs, headers);

        expect(result).toMatchObject({
            data: {
                listCategories: {
                    data: null,
                    error: {
                        code: "NOT_AUTHORIZED",
                        message: 'Not allowed to access "category" entries.'
                    },
                    meta: null
                }
            }
        });
    });

    const notAllowedRwd = [["w"], ["d"], ["wd"]];

    it.each(notAllowedRwd)(`cant get entry - missing "r" permission - having "%s"`, async rwd => {
        // Use "manage" API to create and publish entries
        const { createCategory, publishCategory } = useCategoryManageHandler(manageOpts);

        // Create an entry
        const [create] = await createCategory({ data: { title: "Title 1", slug: "slug-1" } });
        const category = create.data.createCategory.data;
        const { id: categoryId } = category;

        // Publish it so it becomes available in the "read" API
        await publishCategory({ revision: categoryId });

        // See if entries are available via "read" API
        const { getCategory } = useCategoryReadHandler({
            ...readOpts,
            identity: createIdentity([
                {
                    name: "cms.contentEntry",
                    rwd: rwd
                }
            ])
        });

        const queryArgs = {
            where: {
                id: categoryId
            }
        };
        const headers = {
            authorization: API_TOKEN
        };

        const [result] = await getCategory(queryArgs, headers);

        expect(result).toMatchObject({
            data: {
                getCategory: {
                    data: null,
                    error: {
                        code: "NOT_AUTHORIZED",
                        message: 'Not allowed to access "category" entries.'
                    }
                }
            }
        });
    });

    it.each(notAllowedRwd)(
        `cant list entries - missing "r" permission - having "%s"`,
        async rwd => {
            // Use "manage" API to create and publish entries
            const { createCategory, publishCategory } = useCategoryManageHandler(manageOpts);

            // Create an entry
            const [create] = await createCategory({ data: { title: "Title 1", slug: "slug-1" } });
            const category = create.data.createCategory.data;
            const { id: categoryId } = category;

            // Publish it so it becomes available in the "read" API
            await publishCategory({ revision: categoryId });

            // See if entries are available via "read" API
            const { listCategories } = useCategoryReadHandler({
                ...readOpts,
                identity: createIdentity([
                    {
                        name: "cms.contentEntry",
                        rwd: rwd
                    }
                ])
            });

            const queryArgs = {
                where: {
                    id: categoryId
                }
            };
            const headers = {
                authorization: API_TOKEN
            };

            const [result] = await listCategories(queryArgs, headers);

            expect(result).toMatchObject({
                data: {
                    listCategories: {
                        data: null,
                        error: {
                            code: "NOT_AUTHORIZED",
                            message: 'Not allowed to access "category" entries.'
                        },
                        meta: null
                    }
                }
            });
        }
    );
});
